﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using EzDesk.Apps.Data.ProjectModule;
using EzDesk.Apps.Interface.ProjectModule;
using EzDesk.Apps.Model.ProjectModule;
using EzDesk.Apps.ServiceImpl.ProjectModule.History;
using EzDesk.Utility.Json;
using EzDesk.Utility.ServiceStatusInfo;
using EzDesk.Utility.Web.Extensions;
using EzDesk.Utility;
using EzDesk.Core.Model;

namespace EzDesk.Apps.ServiceImpl.ProjectModule
{
    /// <summary>
    /// 项目相关服务
    /// </summary>
    public partial class ProjectService : IProjectService
    {
        

        #region [feed methods]
        private string FeedTemplateStr_project_taskUpdated = "project_taskUpdated";
        private void sendproject_taskUpdated(Task task, string updateContent)
        {
            var project =
                EzDeskProjectContainer.Go(container => container.ProjectSet.FirstOrDefault(p => p.Id == task.ProjectId));
            var feedData = new Dictionary<string, string>
                   {
                      {"projectId", task.ProjectId.ToString()},
                      {"taskTitle", task.Name},
                      {"updateContent", updateContent},
                      {"projectTitle", project.Name}
                   };

            var sendFeedId = _ezApiClient.GetFeedRecord(task.Id, FeedTemplateStr_project_taskUpdated, null);
            if (string.IsNullOrEmpty(sendFeedId))
            {
                // 新发一条 Feed
                var feedId = _ezApiClient.SendFeed(
                    FeedTemplateStr_project_taskUpdated,
                    JsonHelper.ConvertToJson(feedData),
                    project.MemberString
                    );
                _ezApiClient.SetFeedRecord(task.Id, FeedTemplateStr_project_taskUpdated, project.MemberString,
                                           feedId, null);
            }
            else
            {
                var feedId = Guid.Parse(sendFeedId);
                // 之前已经发送过这条Feed
                _ezApiClient.UpdateFeedRecipients(feedId, project.MemberString);
                _ezApiClient.SetFeedRecord(task.Id, FeedTemplateStr_project_taskUpdated, project.MemberString, feedId, null);
                _ezApiClient.UpdateFeedsData(sendFeedId, JsonHelper.ConvertToJson(feedData));
            }
        }

        // 删掉项目计划更新的Feed
        private void removeproject_taskUpdated(Task task)
        {
            var sendFeedId = _ezApiClient.GetFeedRecord(task.Id, FeedTemplateStr_project_taskUpdated, null);
            if (!string.IsNullOrEmpty(sendFeedId))
            {
                _ezApiClient.SetFeedsIsAvailable(sendFeedId, "everyone", false);
            }
        }

        private string FeedTemplateStr_project_inChargeOfTask = "project_inChargeOfTask";
        private void sendproject_inChargeOfTask(Task task)
        {
            var project =
                EzDeskProjectContainer.Go(container => container.ProjectSet.FirstOrDefault(p => p.Id == task.ProjectId));
            var feedData = new Dictionary<string, string>
                   {
                      {"projectId", task.ProjectId.ToString()},
                      {"taskTitle", task.Name},
                      {"projectName", project.Name},
                   };

            _ezApiClient.SendFeed(
                 FeedTemplateStr_project_inChargeOfTask,
                 JsonHelper.ConvertToJson(feedData),
                 task.ChargePersonId.ToString()
                 );
        }

        private string FeedTemplateStr_project_taskExpired = "project_taskExpired";
        private void sendproject_taskExpired(Task task)
        {
            var project =
                EzDeskProjectContainer.Go(container => container.ProjectSet.FirstOrDefault(p => p.Id == task.ProjectId));

            var feedData = new Dictionary<string, string>
                   {
                      {"projectId", task.ProjectId.ToString()},
                      {"taskTitle", task.Name},
                      {"projectName", project.Name},
                   };

            _ezApiClient.SendFeed(
                 FeedTemplateStr_project_taskExpired,
                 JsonHelper.ConvertToJson(feedData),
                 task.ChargePersonId.ToString()
                 );
        }
        private string FeedTemplateStr_project_workitemsTaskExpired = "project_workitemsTaskExpired";
        private void sendproject_workitemsTaskExpired(List<Workitem> workitems, Task task, Project project)
        {
            if (!workitems.First().OwnerId.HasValue) return;
            var memberstring = workitems.First().OwnerId.ToString();
            var feedData = new Dictionary<string, string>
                   {
                      {"projectTitle", project.Name},
                      {"taskTitle", task.Name},
                      {"workitemNames", workitems.Select(p=>p.Title).Join(",")},
                   };

            _ezApiClient.SendFeed(
                 FeedTemplateStr_project_workitemsTaskExpired,
                 JsonHelper.ConvertToJson(feedData),
                 memberstring
                 );
        }



        #endregion


        private Task createTask(Task task, Guid actorId)
        {
            EzDeskProjectContainer.GoAndSave(container =>
            {
                task.Id = Guid.NewGuid();
                task.CreateOn = DateTime.Now;
                container.TaskSet.AddObject(task);
                container.SaveChanges();

                // 更新项目历史
                var content = HistoryManager.CreateHistory(new CreateTaskHistoryRecorder(task.Id, actorId));
                // 发送项目计划更新 Feed
                sendproject_taskUpdated(task, content);
                // 给项目负责人特地发一条feed
                if (task.ChargePersonId.HasValue && actorId != task.ChargePersonId.Value)
                {
                    sendproject_inChargeOfTask(task);
                }
            });
            return task;
        }

        public StatusInfo<Task> CreateTask(Task task, Guid actorId)
        {
            return StatusInfo.Wrap(() => createTask(task, actorId));
        }

        public StatusInfo DeleteTask(Guid taskId, Guid actorId)
        {
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.GoAndSave(db =>
            {
                Action<Task> removeTask = null;
                removeTask = task =>
                {
                    // 删除掉项目计划更新的Feed
                    removeproject_taskUpdated(task);
                    var subTasks = db.TaskSet.Where(st => st.ParentTaskId == task.Id);
                    foreach (var subTask in subTasks)
                    {
                        removeTask(subTask);
                    }
                    foreach (var workitem in db.WorkitemSet.Where(wi => wi.TaskId == task.Id))
                    {
                        db.WorkitemSet.DeleteObject(workitem);
                    }
                    db.TaskSet.DeleteObject(task);
                };
                var taskToBeRemoved = db.TaskSet.First(t => t.Id == taskId);

                // 先记录下删除Task的项目历史
                var content = HistoryManager.CreateHistory(
                    new DeleteTaskHistoryRecorder(taskId, actorId));
                // 更新项目的feed
                sendproject_projectUpdated(taskToBeRemoved.ProjectId, content);
                removeTask(taskToBeRemoved);

                db.SaveChanges();
            }));
        }


        private void setTaskStartOn(EzDeskProjectContainer db, Task task, TimeSpan dateDiff, bool? moveChild)
        {
            if (dateDiff.TotalDays == 0) return;
            task.StartOn = task.StartOn.Value.Add(dateDiff);

            if(moveChild.HasValue && moveChild.Value)
            {
                var subTasks = db.TaskSet.Where(t => t.ParentTaskId == task.Id);
                subTasks.Foreach(t => setTaskStartOn(db, t, dateDiff, moveChild));
            }
            
        }

        public StatusInfo UpdateTask(Task newTask, bool? moveChild, Guid actorId)
        {
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.GoAndSave(db =>
            {
                Task task = db.TaskSet.FirstOrDefault(t => t.Id == newTask.Id);

                EzDeskAppApiHelper.DelaySendUpdateFeeds(task, newTask, actorId, 
                    p => p.ActorId == actorId && p.OldObject.Id == task.Id,
                    sendTaskUpdateFeeds);

                task.ChargePersonId = newTask.ChargePersonId;
                task.Duration = newTask.Duration;
                task.MasterTaskId = newTask.MasterTaskId;
                task.Name = newTask.Name;
                task.ParentTaskId = newTask.ParentTaskId;
                task.ProjectId = newTask.ProjectId;

                var dateDiff = newTask.StartOn.Value - task.StartOn.Value;
                setTaskStartOn(db, task, dateDiff, moveChild);
            }));
        }

        

        private void sendTaskUpdateFeeds(Task oldTask, Task newTask, Guid actorId)
        {
            var content = HistoryManager.CreateHistory(
                new ContentUpdatedTaskHistoryRecorder(oldTask, newTask, actorId));
            // 更新项目计划Feed
            sendproject_taskUpdated(oldTask, content);
            if (oldTask.ChargePersonId.HasValue)
            {
                // todo: 这里需要给计划的负责人单独发一条notification feed,但是在这之前必须解决合并发送feed的问题
                // 
            }
            // 给任务负责人发一个通知
            if (oldTask.ChargePersonId != newTask.ChargePersonId && newTask.ChargePersonId.HasValue)
            {
                sendproject_inChargeOfTask(newTask);
            }
        }

        public StatusInfo CheckTimeoutTasks()
        {
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.Go(container =>
            {
                var now = DateTime.Now;
                var taskList = container.TaskSet
                    .Include("Workitem")
                    .Where(task => task.StartOn < now && task.Workitem.Count > 0).ToList();

                foreach (var task in taskList)
                {
                    if (task.StartOn.Value + TimeSpan.FromHours(task.Duration * 3) != DateTime.Today)
                    {
                        continue;
                    }
                    var unfinishedWorkitems = task.Workitem.Where(wi => wi.Status != WorkitemStatus.Completed);
                    if (unfinishedWorkitems.Count() == 0) continue;

                    // 把未完成的workitems按负责人分类
                    var project = container.ProjectSet.First(p => p.Id == task.ProjectId);
                    var unfinishedGroup = unfinishedWorkitems.GroupBy(p => p.OwnerId);

                    foreach (var workitemGroup in unfinishedGroup)
                    {
                        // 为每个没有完成的workitem发送feed
                        sendproject_workitemsTaskExpired(workitemGroup.ToList(), task, project);
                    }
                    // 记录项目计划过期的项目历史
                    var content = HistoryManager.CreateHistory(new TaskExpiredHistoryRecorder(task.Id));
                    // 发现项目计划更新的feed
                    sendproject_taskUpdated(task, content);
                    // 给项目计划的负责人单独发一条feed
                    if (task.ChargePersonId.HasValue)
                    {
                        sendproject_taskExpired(task);
                    }

                }
                container.SaveChanges();
            }));
        }

        public StatusInfo<List<Workitem>> GetTaskWorkitems(Guid taskId)
        {
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.Go(db => db.WorkitemSet.Where(wi => wi.TaskId == taskId).ToList()));
        }

        public StatusInfo<List<Task>> GetPorjectTasks(Guid projectId)
        {
            return StatusInfo.Wrap(() => GetFullProjectById(projectId).Result.RootTasks.ToList());
        }

        public StatusInfo CreateTaskWithTemplate(Guid projectId, Guid? parentTaskId, Guid taskTemplateId, Guid actorId)
        {
            return StatusInfo.Wrap(() => EzDeskProjectContainer.GoAndSave(db =>
            {
                var template = db.TaskTemplateSet.FirstOrDefault(t => t.Id == taskTemplateId);
                if (template == null) throw new Exception("没有该模板");

                var parentTask = db.TaskSet.FirstOrDefault(t => t.Id == parentTaskId);
                var project = db.ProjectSet.FirstOrDefault(p => p.Id == projectId);

                DateTime startOn;
                if (parentTask != null)
                    startOn = parentTask.StartOn.Value;
                else if (project.Task.Count == 0)
                    startOn = DateTime.Today;
                else
                    startOn = project.Task.Min(t => t.StartOn.Value);

                var taskList = WebJsonExtension.FromJson<List<Task>>(template.TemplateData);

                var beginDate = taskList[0].StartOn.Value;
                var dateDiff = startOn - beginDate;
                var endDate = startOn + TimeSpan.FromHours(taskList[0].Duration * 3);

                var cacheIdMapper = taskList.ToDictionary(taskData => taskData.Id, taskData => Guid.NewGuid());

                foreach (var task in taskList)
                {
                    task.Id = cacheIdMapper[task.Id];
                    if (task.MasterTaskId.HasValue)
                        task.MasterTaskId = cacheIdMapper[task.MasterTaskId.Value];
                    if (task.ParentTaskId.HasValue)
                        task.ParentTaskId = cacheIdMapper[task.ParentTaskId.Value];
                    else if (parentTaskId.HasValue)
                    {
                        task.ParentTaskId = parentTaskId;
                    }

                    task.ProjectId = projectId;
                    task.StartOn += dateDiff;

                    db.TaskSet.AddObject(task);

                    var content = HistoryManager.CreateHistory(
                        new ApplyTempalteProjectHistoryRecorder(project, actorId, template));
                    sendproject_projectUpdated(projectId, content);
                }


                var workitemList =  WebJsonExtension.FromJson<List<Workitem>>(template.WorkitemsData);
                var actor = _ezApiClient.GetSimpleUserById(actorId);

                foreach (var workitem in workitemList)
                {
                    workitem.Id = Guid.NewGuid();
                    workitem.ProjectId = projectId;
                    if (workitem.TaskId.HasValue) workitem.TaskId = cacheIdMapper[workitem.TaskId.Value];

                    workitem.OwnerId = null;
                    workitem.OwnerName = null;
                    
                    workitem.CreatorId = actor.Id;
                    workitem.CreatorName = actor.Name;

                    workitem.CreateOn = DateTime.Now;
                    workitem.UpdateOn = DateTime.Now;
                    
                    workitem.Status = WorkitemStatus.Ready;
                    workitem.FinishOn = null;
                    workitem.IsFinished = false;
                    workitem.IsAvailable = true;

                    workitem.PlanOn = null;

                    db.WorkitemSet.AddObject(workitem);
                }

                if (parentTaskId.HasValue)
                    ExtendParentTask(endDate, parentTaskId.Value, db);
            }));
        }

        private void ExtendParentTask(DateTime endDate, Guid taskId, EzDeskProjectContainer db)
        {
            var task = db.TaskSet.First(t => t.Id == taskId);
            if (task.GetEndDate() < endDate)
            {
                task.Duration = (endDate - task.StartOn.Value).TotalDays * 8;
                if (task.ParentTaskId.HasValue)
                    ExtendParentTask(endDate, task.ParentTaskId.Value, db);
            }
        }

        public StatusInfo<List<Task>> GetTasksInProjects(List<Guid> projectIdList)
        {
            return StatusInfo.Wrap(
                        () => EzDeskProjectContainer
                            .GoAndSave(db => db.TaskSet
                                .Where(p => projectIdList.Contains(p.ProjectId))
                                .ToList()));
        }

        public StatusInfo<List<TaskTemplate>> GetTaskTemplateList()
        {
            return StatusInfo.Wrap(() => EzDeskProjectContainer.Go(db => db.TaskTemplateSet.OrderByDescending(tt => tt.CreateOn).ToList()));
        }

        public StatusInfo<TaskTemplate> CreateTaskTemplate(TaskTemplate taskTemplate)
        {
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.GoAndSave(db =>
            {
                taskTemplate.Id = Guid.NewGuid();
                taskTemplate.CreateOn = DateTime.Now;
                db.TaskTemplateSet.AddObject(taskTemplate);
                db.SaveChanges();
                return taskTemplate;
            }));
        }

        public StatusInfo RemoveTaskTemplate(Guid id)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer.GoAndSave(
                    db =>
                    db.TaskTemplateSet
                      .DeleteObject(db.TaskTemplateSet.FirstOrDefault(pt => pt.Id == id))
                ));
        }

        
    }


}
